package com.nineclock.sign.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nineclock.common.entity.PageResult;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.common.utils.JsonUtils;
import com.nineclock.sigb.dto.SignAllUserDTO;
import com.nineclock.sigb.dto.SignDTO;
import com.nineclock.sigb.dto.SignInfoDTO;
import com.nineclock.sigb.dto.SignPCListDTO;
import com.nineclock.sigb.pojo.SignInfo;
import com.nineclock.sigb.pojo.SignPicture;
import com.nineclock.sign.mapper.SignInfoMapper;
import com.nineclock.sign.mapper.SignPictureMapper;
import com.nineclock.sign.service.SignInfoService;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.dto.SysDepartmentDTO;
import com.nineclock.system.feign.SysOrganizationFeign;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ：zhouc
 * @date ：Created in 2021/6/28
 * @description ：
 * @version: 1.0
 */
@Slf4j
@Service
@SuppressWarnings({"all"})
public class SignInfoServiceImpl implements SignInfoService {
    
    @Autowired
    SignInfoMapper signInfoMapper;
    @Autowired
    SignPictureMapper signPictureMapper;
    
    @Autowired
    RedisTemplate<String, String> redisTemplate;
    
    public void signAttend(SignDTO signDTO) {
        //健壮性判断
        if (signDTO == null) {
            throw new NcException(ResponseEnum.INVALID_USERNAME_PASSWORD);
        }
        //赋值DTO 为 pojo
        SignInfo signInfo = BeanHelper.copyProperties(signDTO, SignInfo.class);
        //封装数据到signInfo
        signInfo.setCompanyId(CurrentUserHolder.get().getCompanyId());
        signInfo.setCompanyUserId(CurrentUserHolder.get().getCompanyUserId());
        signInfo.setCreateTime(new Date());
        signInfo.setCreateDate(DateUtil.format(new Date(), "yyyy-MM-dd"));
        signInfo.setCreateMonth(DateUtil.format(new Date(), "yyyy-MM"));
        signInfoMapper.insert(signInfo);
        Long id = signInfo.getId();
        SignPicture signPicture = new SignPicture();
        String images = signDTO.getImages();
        String[] split = images.split(",");
        for (String s : split) {
            signPicture.setSignId(id);
            signPicture.setUrl(s);
            signPictureMapper.insert(signPicture);
        }
        //查询当日签到次数
        LambdaQueryWrapper<SignInfo> signDayCount = new LambdaQueryWrapper<>();
        signDayCount.eq(SignInfo::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        signDayCount.eq(SignInfo::getCreateDate, DateUtil.format(new Date(), "yyyy-MM-dd"));
        Integer integer = signInfoMapper.selectCount(signDayCount);
        //存redis
        String day = DateUtil.format(new Date(), "yyyy-MM-dd");
        String key = CurrentUserHolder.get().getMobile() + day;
        redisTemplate.boundValueOps(key).set(Integer.toString(integer));
        //查询当月签到次数
        LambdaQueryWrapper<SignInfo> signMonthCount = new LambdaQueryWrapper<>();
        signMonthCount.eq(SignInfo::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        signMonthCount.eq(SignInfo::getCreateMonth, DateUtil.format(new Date(), "yyyy-MM"));
        Integer integer1 = signInfoMapper.selectCount(signMonthCount);
        //存redis
        String month = DateUtil.format(new Date(), "yyyy-MM");
        String key1 = CurrentUserHolder.get().getMobile() + month;
        redisTemplate.boundValueOps(key1).set(Integer.toString(integer1));
    }
    
    public List<SignDTO> SignCurrentUserByMonth(String dateStr) {
        if (dateStr == null) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        ArrayList<SignDTO> list = new ArrayList<>();
        //SignDTO signDTO = new SignDTO();
        //SignPicture signPicture = new SignPicture();
        LambdaQueryWrapper<SignInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SignInfo::getCreateMonth, dateStr);
        List<SignInfo> signInfoList = signInfoMapper.selectList(wrapper);
        for (SignInfo signInfo : signInfoList) {
            LambdaQueryWrapper<SignPicture> wrapper1 = new LambdaQueryWrapper<>();
            SignDTO signDTO1 = BeanHelper.copyProperties(signInfo, SignDTO.class);
            signDTO1.setUserName(CurrentUserHolder.get().getUsername());
            Long id = signInfo.getId();
            wrapper1.eq(SignPicture::getSignId, id);
            //根据签到id得到 图片表对应得集合
            List<SignPicture> signPictureList = signPictureMapper.selectList(wrapper1);
            List<String> listPic = new ArrayList<>();
            //for (SignPicture picture : signPictureList) {
            //    String url = picture.getUrl();
            //    listPic.add(url);
            //}
            listPic = signPictureList.stream().map(signPicture -> {
                return signPicture.getUrl();
            }).collect(Collectors.toList());
            signDTO1.setSignPictureUrlList(listPic);
            String images = listPic.stream().collect(Collectors.joining(","));
            signDTO1.setImages(images);
            String key = CurrentUserHolder.get().getMobile() + DateUtil.format(new Date(), "yyyy-MM-dd");
            String s = redisTemplate.boundValueOps(key).get();
            signDTO1.setDayCount(s);
            String key1 = CurrentUserHolder.get().getMobile() + DateUtil.format(new Date(), "yyyy-MM");
            String s1 = redisTemplate.boundValueOps(key1).get();
            signDTO1.setMonthCount(s1);
            list.add(signDTO1);
        }
        return list;
    }
    //@Autowired
    //private RedisTemplate redisTemplate;
    
    /**
     * 导出签到列表
     *
     * @param startTime
     * @param endTime
     */
    public void exportExcel(String startTime, String endTime) throws IOException {
        //todo 获取到所有员工的签到数据集合 后续更改
        //ArrayList list = new ArrayList();
        //List<String> dateList = this.getSpanDateList(startTime, endTime);
        //查询该企业下的所有的员工信息
        //List<SysCompanyUserDTO> companyUserDTOList = organizationFeign.queryAllCompanyUser().getData();
        //List<SignPCListDTO> list = this.getSignInfo(companyUserDTOList, startTime, endTime, dateList);
        Long companyId = CurrentUserHolder.get().getCompanyId();
        String o = (String) redisTemplate.boundValueOps(companyId + "").get();
        List<SignPCListDTO> list = JsonUtils.toList(o, SignPCListDTO.class);
        //设置响应
        RequestAttributes requestAttributes =
            RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        //获取响应对象
        HttpServletResponse response = servletRequestAttributes.getResponse();
        response.setHeader("Content-disposition", "attachment;filename=Attendance.xls");
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        EasyExcel.write(response.getOutputStream())
            .registerWriteHandler(styleStrategy())
            .head(head(list))
            .registerWriteHandler(new MyColumnStyle())
            .sheet("第一个工作表")
            .doWrite(data(list)); //执行写操作
    }
    
    public HorizontalCellStyleStrategy styleStrategy() {
        //写入excel
        WriteCellStyle cellStyle = new WriteCellStyle();
        //表头和内容都是水平居中
        cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //设置字体
        WriteFont font = new WriteFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 12);
        cellStyle.setWriteFont(font);
        HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(cellStyle, cellStyle);
        return styleStrategy;
    }
    
    /**
     * 操作excel表头
     *
     * @param list
     * @return
     */
    public List<List<String>> head(List<SignPCListDTO> list) {
        List<List<String>> head = new ArrayList<>();
        head.add(Arrays.asList("姓名"));
        head.add(Arrays.asList("工号"));
        head.add(Arrays.asList("部门"));
        head.add(Arrays.asList("职位"));
        //添加 日期表头
        if (CollectionUtil.isNotEmpty(list)) {
            //获取 第一个员工的签到 情况
            SignPCListDTO signPCListDTO = list.get(0);
            //获取指定时间段的签到情况
            List<SignInfoDTO> attendsList = signPCListDTO.getSigns();
            if (CollectionUtil.isNotEmpty(attendsList)) {
                for (SignInfoDTO attendsDTO : attendsList) {
                    head.add(Arrays.asList(attendsDTO.getSignDate()));
                }
            }
        }
        return head;
    }
    
    /**
     * 操作excel数据
     *
     * @param attendPunchDTOList
     * @return
     */
    public List<List<String>> data(List<SignPCListDTO> signPCListDTOList) {
        List<List<String>> data = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(signPCListDTOList)) {
            //循环每个员工的签到情况
            for (SignPCListDTO signPCListDTO : signPCListDTOList) {
                //对应每个员工的签到（对应一行数据）
                List<String> list = new ArrayList<>();
                list.add(signPCListDTO.getUsername());
                list.add(signPCListDTO.getWorkNumber()); //工号
                list.add(this.getDepartName(signPCListDTO));
                list.add(signPCListDTO.getPost()); //职位
                //每个员工的签到情况
                List<SignInfoDTO> signInfoDTOList = signPCListDTO.getSigns();
                if (CollectionUtil.isNotEmpty(signInfoDTOList)) {
                    for (SignInfoDTO signInfoDTO : signInfoDTOList) {
                        list.add(signInfoDTO.getSignCount() + "");
                    }
                }
                data.add(list);
            }
        }
        return data;
    }
    
    class MyColumnStyle extends AbstractColumnWidthStyleStrategy {
        
        protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList,
            Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
            writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 4500);
        }
    }
    
    /**
     * 根据用户名查询部门名称
     */
    public String getDepartName(SignPCListDTO signPCListDTO) {
        Long companyId1 = CurrentUserHolder.get().getCompanyId();
        String username = signPCListDTO.getUsername();
        String departmentName = organizationFeign.queryDepartmentName(username, companyId1).getData();
        return departmentName;
    }
    //@Autowired
    //private SignInfoMapper signInfoMapper;
    
    @Autowired
    SysOrganizationFeign organizationFeign;
    
    //获取当日签到次数
    public Integer getDayCount() {
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();
        String taday = DateTimeUtil.dateToStr(new Date(), "yyyy-MM-dd");
        LambdaQueryWrapper<SignInfo> signInfoQw = new LambdaQueryWrapper<>();
        signInfoQw.eq(SignInfo::getCompanyUserId, companyUserId);
        signInfoQw.eq(SignInfo::getCreateDate, taday);
        Integer count = signInfoMapper.selectCount(signInfoQw);
        return count;
    }
    
    //分页查询签到列表
    public PageResult<SignPCListDTO> QueryAllSign(String startTime, String endTime, Integer page, Integer pageSize) {
        //获取 时间段内的 日期集合
        List<String> dateStrList = this.getSpanDateList(startTime, endTime);
        //查询该企业下的所有的员工信息
        List<SysCompanyUserDTO> companyUserDTOList = organizationFeign.queryAllCompanyUser().getData();
        //查询每个员工该时间范围内所有的签到情况
        List<SignPCListDTO> signPCListDTOList = this.getSignInfo(companyUserDTOList, startTime, endTime, dateStrList);
        //根据前端传给的page,pageSize,返回给前端相关数据.
        Long total = (long) companyUserDTOList.size();
        long totalPage = (total + pageSize - 1) / pageSize;
        List<SignPCListDTO> signPCListDTOList1 = new ArrayList<>();
        for (int i = (page - 1) * pageSize; i < page * pageSize; i++) {
            if (i < total) {
                signPCListDTOList1.add(signPCListDTOList.get(i));
            }
        }
        if (signPCListDTOList != null && signPCListDTOList.size() > 0) {
            return new PageResult(total, totalPage, signPCListDTOList1);
        }
        return new PageResult();
    }
    
    /**
     * 获取 时间段内的 日期集合
     */
    public List<String> getSpanDateList(String startTime, String endTime) {
        //获取 时间 段内的每一天 日期 集合
        List<Date> dateList = DateTimeUtil.getDatesBetweenTwoDate(startTime, endTime, "yyyy-MM-dd");
        //转换为  字符串日期格式的集合
        List<String> dateStrList = dateList.stream().map(date -> {
            return DateUtil.format(date, "yyyy-MM-dd");
        }).collect(Collectors.toList());
        return dateStrList;
    }
    
    //查询每个员工该时间范围内所有的签到情况(在指定时间段内)
    public List<SignPCListDTO> getSignInfo(List<SysCompanyUserDTO> companyUserDTOList, String startTime, String endTime, List<String> dateStrList) {
        List<SignPCListDTO> signs = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(companyUserDTOList)) {
            //循环每个员工
            for (SysCompanyUserDTO companyUserDTO : companyUserDTOList) {
                SignPCListDTO signPCListDTO = BeanHelper.copyProperties(companyUserDTO, SignPCListDTO.class);
                Long id = companyUserDTO.getId();
                LambdaQueryWrapper<SignInfo> signInfoQw = new LambdaQueryWrapper<>();
                signInfoQw.eq(SignInfo::getCompanyUserId, id);
                signInfoQw.between(SignInfo::getCreateDate, startTime, endTime);
                List<SignInfo> signInfoList = signInfoMapper.selectList(signInfoQw);
                Map<String, List<SignInfo>> signInfomap = signInfoList.stream().collect(Collectors.groupingBy(SignInfo::getCreateDate));
                //存储每个员工每天的签到情况
                List<SignInfoDTO> signInfoDTOList = new ArrayList<>();
                for (String date : dateStrList) {
                    //每天对应一个 SignInfoDTO对象
                    SignInfoDTO signInfoDTO = new SignInfoDTO();
                    //员工 某一天对应的考勤情况
                    List<SignInfo> attendPunches = signInfomap.get(date);
                    //获取当日签到次数
                    LambdaQueryWrapper<SignInfo> daysign = new LambdaQueryWrapper<>();
                    daysign.eq(SignInfo::getCompanyUserId, id);
                    daysign.eq(SignInfo::getCreateDate, date);
                    Integer count = signInfoMapper.selectCount(daysign);
                    //设置日期和数量
                    signInfoDTO.setSignCount(count);
                    signInfoDTO.setSignDate(date);
                    //添加到 集合中
                    signInfoDTOList.add(signInfoDTO);
                }
                //把员工签到的情况放到签到对象里
                signPCListDTO.setSigns(signInfoDTOList);
                Long departmentId = companyUserDTO.getDepartmentId();
                SysDepartmentDTO departmentDTO = null;
                if (departmentId != null) {
                    departmentDTO = organizationFeign.queryDepartmentBycompanyUserId(departmentId);
                }
                signPCListDTO.setDepartment(departmentDTO);
                String userName = companyUserDTO.getUserName();
                signPCListDTO.setUsername(userName);
                signs.add(signPCListDTO);
            }
        }
        //todo 发现效率太慢，存到redis缓存中
        String key = CurrentUserHolder.get().getCompanyId() + "";
        String toString = JsonUtils.toString(signs);
        redisTemplate.boundValueOps(key).set(toString);
        return signs;
    }
  
    @Override
    public List<SignAllUserDTO> listSignAllUser(String dateStr) {
        Long companyId = CurrentUserHolder.get().getCompanyId();
        LambdaQueryWrapper<SignInfo> signInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        signInfoLambdaQueryWrapper.eq(SignInfo::getCreateDate, dateStr);
        signInfoLambdaQueryWrapper.eq(SignInfo::getCompanyId, companyId);
        List<SignInfo> signInfos = signInfoMapper.selectList(signInfoLambdaQueryWrapper);
        Map<Long, List<SignInfo>> collect = signInfos.stream().collect(Collectors.groupingBy(SignInfo::getCompanyUserId));
        ArrayList<SignAllUserDTO> signAllUserDTOS = new ArrayList<>();
        for (Long key : collect.keySet()) {
            SignAllUserDTO signAllUserDTO = new SignAllUserDTO();
            SysCompanyUserDTO companyUserDTO = organizationFeign.queryNameById(key).getData();
            signAllUserDTO.setUsername(companyUserDTO.getUserName());
            List<SignDTO> signDTOS = BeanHelper.copyWithCollection(collect.get(key), SignDTO.class);
            for (SignDTO signDTO : signDTOS) {
                signDTO.setDayCount(collect.get(signDTO.getCompanyUserId()).size() + "");
                LambdaQueryWrapper<SignPicture> signPictureLambdaQueryWrapper = new LambdaQueryWrapper<>();
                signPictureLambdaQueryWrapper.eq(SignPicture::getSignId,signDTO.getId());
                List<SignPicture> signPictures = signPictureMapper.selectList(signPictureLambdaQueryWrapper);
                List<String> listUrl = signPictures.stream().map(signPicture -> {
                    return signPicture.getUrl();
                }).collect(Collectors.toList());
                signDTO.setSignPictureUrlList(listUrl);
            }
            signAllUserDTO.setSignResponseList(signDTOS);
            signAllUserDTOS.add(signAllUserDTO);
        }
        return signAllUserDTOS;
    }
}
